

using System;



namespace SharpSha
{
	
	/// <summary>
	/// Classe de traitement sha
	/// </summary>
	public class Sha
	{
		const uint H0 = 0x67452301;
		const uint H1 = 0xefcdab89;
		const uint H2 = 0x98badcfe;
		const uint H3 = 0x10325476;
		const uint H4 = 0xc3d2e1f0;

		const uint K0 = 0x5a827999;
		const uint K1 = 0x6ed9eba1;
		const uint K2 = 0x8f1bbcdc;
		const uint K3 = 0xca62c1d6;

		const byte PAD  = 0x80;
		const byte ZERO =	0;

		uint[]			length	= new uint[2];
		uint[]			h		= new uint[5];
		uint[]			w		= new uint[80];
		byte[]			hash	= new byte[20];	

		/* functions */

		uint S(int n,uint x) {	return (((x)<<n) | ((x)>>(32-n)));}

		uint	 F0(uint x,uint y,uint z) {	return (z^(x&(y^z)));}
		uint	 F1(uint x,uint y,uint z) {	return (x^y^z);}
		uint	 F2(uint x,uint y,uint z) {	return ((x&y) | (z&(x|y))) ;}
		uint	 F3(uint x,uint y,uint z) {	return (x^y^z);}

		

		public Sha(byte[] src)
		{ 
		    
			for (int i=0 ; i < 80 ; i++) 
				w[i]	=	0;
		    
			length[0]	=	length[1]	=	0;
			h[0]		=	H0;
			h[1]		=	H1;
			h[2]		=	H2;
			h[3]		=	H3;
			h[4]		=	H4;

			int sz		=	src.Length;
			for ( int cnt=0 ; cnt< sz  ; cnt++ )
			{
				NextBlock(src[cnt]);
			}
			LastBlock();
		}

		public byte[]	Result
		{
			get{	return hash;}
		}


		/// <summary>
		/// Process the next message byte 
		/// </summary>
		/// <param name="Byte"></param>
		void NextBlock(byte xByte)
		{ 
			uint cnt		=	((length[0]/32)%16);
		    
			w[cnt]		<<=	8;
			w[cnt]		|=	xByte;

			length[0]		+=	8;
			if ( length[0]==0L ) 
			{ 
				length[1]++;
				length[0]	=	0; 
			}
			if ( (length[0]%512) == 0 ) 
				sha_transform();
		}

		/// <summary>
		///  Pad message and finish - supply 20 byte digest 
		/// </summary>
		void LastBlock()
		{ 
			byte	len0	=	(byte)length[0];
			byte	len1	=	(byte)length[1];
			NextBlock(PAD);
			while ((length[0]%512)!=448) 
				NextBlock(ZERO);
			w[14]			=	len1;
			w[15]			=	len0;    
			sha_transform();
			order();
			
			// convert to bytes 
			for (int i=0;i<20;i++)
			{ 
				hash[i]=(byte)((h[i/4]>>(8*(i%4))) & 0xff);
			};
		    
		}

		/// <summary>
		/// basic transformation step 
		/// </summary>
		void sha_transform()
		{ 
			for (int t=16;t<80;t++) 
					w[t]=(byte)S(1,(uint)(w[t-3]^w[t-8]^w[t-14]^w[t-16]));
			uint a=h[0]; uint b=h[1]; uint c=h[2]; uint d=h[3]; uint e=h[4];
			for (int t=0;t<20;t++)
			{ /* 20 times - mush it up */
				uint temp=K0+F0(b,c,d)+S(5,a)+e+w[t];
				e=d; d=c;
				c=S(30,b);
				b=a; a=temp;
			}
			for (int t=20;t<40;t++)
			{ /* 20 more times - mush it up */
				uint temp=K1+F1(b,c,d)+S(5,a)+e+w[t];
				e=d; d=c;
				c=S(30,b);
				b=a; a=temp;
			}
			for (int t=40;t<60;t++)
			{ /* 20 more times - mush it up */
				uint temp=K2+F2(b,c,d)+S(5,a)+e+w[t];
				e=d; d=c;
				c=S(30,b);
				b=a; a=temp;
			}
			for (int t=60;t<80;t++)
			{ /* 20 more times - mush it up */
				uint temp=K3+F3(b,c,d)+S(5,a)+e+w[t];
				e=d; d=c;
				c=S(30,b);
				b=a; a=temp;
			}
			h[0]+=a; 
			h[1]+=b; 
			h[2]+=c;
			h[3]+=d; 
			h[4]+=e;
		} 

		void order ()
		{
			uint 	t	= 0xABCD;
		    
			if ((t&0xFF) == 0xCD)
			{
    			for (int i=0; i < 5 ; i++)
        			h[i] = ((h[i] & 0x000000FF) << 24) +
						((h[i] & 0x0000FF00) << 8) +
						((h[i] & 0x00FF0000) >> 8) +
						((h[i] & 0xFF000000) >> 24);
			};
		}
	}
}
